package cc.zzone.client;

import cc.zzone.domain.ChatConf;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.handshake.ServerHandshake;
import org.springframework.web.client.RestTemplate;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 斗鱼TV弹幕服务监听器
 *
 * @author jinzhengze
 * @since 2023年10月20日
 */
@Slf4j
public class DouyuWsClient extends MyWsClient {

    private final Map<String, Object> roomData;
    private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private DouyuWsClient(URI serverUri, Map<String, String> httpHeaders, String key, Map<String, Object> roomData) {
        super(serverUri, httpHeaders, key);
        this.roomData = roomData;
    }

    public static DouyuWsClient getInstance(ChatConf conf) throws Exception {
        HashMap<String, Object> roomData = new HashMap<>();
        roomData.put("key", conf.getKey());
        roomData.put("roomId", conf.getRoom());
        URI uri = new URI("wss://danmuproxy.douyu.com:8506");
        return new DouyuWsClient(uri, new HashMap<>(), conf.getKey(), roomData);
    }

    @Override
    public void onOpen(ServerHandshake serverHandshake) {
        log.info("【{}】，发送认证请求", roomData.get("key"));
        try {
            // 登录请求
            String loginMsg = String.format("type@=loginreq/roomid@=%s/", roomData.get("roomId"));
            this.send(msgEncode(loginMsg));
            // 加入群组请求
            String joinGroupMsg = String.format("type@=joingroup/rid@=%s/gid@=-9999/", roomData.get("roomId"));
            this.send(msgEncode(joinGroupMsg));
            this.send(msgEncode("type@=sub/mt@=ovl/")); // 暂时未知用途
            this.send(msgEncode("type@=sub/mt@=dayrk/")); // 暂时未知用途
            this.send(msgEncode("type@=unsub/mt@=ovl/")); // 暂时未知用途
        } catch (IOException ignored) {
        }
    }

    @Override
    public void onMessage(ByteBuffer byteBuffer) {
        // 开启心跳线程
        if (getHeartBeatThread() == null || !getHeartBeatThread().isAlive()) {
            setHeartBeatThread(new Thread(() -> {
                while (Thread.currentThread().isAlive()) {
                    log.info("【{}】，发送心跳包", roomData.get("key"));
                    try { // 延时30s，定时发送心跳包
                        if (this.isOpen()) this.send(msgEncode("type@=mrkl/"));
                        else break;
                        TimeUnit.SECONDS.sleep(45);
                    } catch (Exception ignored) {
                    }
                }
            }));
        }
        JSONArray array = msgDecode(byteBuffer);
        log.info("【{}】，消息解析完成。result={}", roomData.get("key"), array);
        formatMsgAndSend(array);
    }

    /**
     * 消息编码器
     */
    private byte[] msgEncode(String message) throws IOException {
        int length = message.length() + 9; // 4字节小端整数，表示整条消息（包括自身）长度（字节数）。
        int send = 689; // 689 客户端发送给弹幕服务器的文本格式数据,暂时未用，默认为 0。保留字段：暂时未用，默认为 0。
        byte[] msgBytes = message.getBytes(StandardCharsets.UTF_8);
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        bytes.write(intToBytesLittle(length));
        bytes.write(intToBytesLittle(length));
        bytes.write(intToBytesLittle(send));
        bytes.write(msgBytes);
        bytes.write(new byte[]{0});
        return bytes.toByteArray();
    }

    /**
     * int转小端模式byte[]
     */
    private byte[] intToBytesLittle(int value) {
        return new byte[]{
                (byte) (value & 0xFF),
                (byte) ((value >> 8) & 0xFF),
                (byte) ((value >> 16) & 0xFF),
                (byte) ((value >> 24) & 0xFF)
        };
    }

    /**
     * 消息解码器
     */
    private JSONArray msgDecode(ByteBuffer byteBuffer) {
        Charset charset = StandardCharsets.UTF_8;
        CharBuffer charBuffer = charset.decode(byteBuffer);
        JSONArray result = new JSONArray();
        String content = charBuffer.toString();
        String[] split = content.split("type@=");
        if (split.length < 2) return result;
        for (int i = 1; i < split.length; i++) {
            int lastIndex = split[i].lastIndexOf("/");
            String msg = "type@=" + split[i].substring(0, lastIndex);
            String[] msgList = msg.split("/"); // 按照/分隔出键值对数据
            JSONObject item = new JSONObject();
            for (String kv : msgList) {
                String[] kvArr = kv.split("@=");
                String key = kvArr[0];
                if (kvArr.length == 1) {
                    item.put(key, "");
                    continue;
                }
                String val = kvArr[1].replaceAll("@S", "/").replaceAll("@A", "@");
                item.put(key, val);
            }
            result.add(item);
        }
        return result;
    }

    /**
     * 格式化数据，并转发给所有连接的ws客户端
     */
    private void formatMsgAndSend(JSONArray array) {
        if (array == null || array.size() == 0) return;
        for (Object o : array) {
            JSONObject item = new JSONObject();
            JSONObject tmp = JSON.parseObject(JSON.toJSONString(o));
            String type = tmp.getString("type");
            // 弹幕消息
            if ("chatmsg".equals(type)) {
                item.put("cmd", "MSG");
                item.put("username", tmp.get("nn"));
                item.put("face", "https://apic.douyucdn.cn/upload/" + tmp.get("ic"));
                item.put("content", tmp.get("txt"));
                item.put("time", sdf.format(new Date()));
                item.put("nameColor", null);
                item.put("contentColor", null);
                item.put("identities", new JSONArray());
                item.getJSONArray("identities").add(0);
            }
            // 进入房间 / 分享房间
            else if ("uenter".equals(type) || "srres".equals(type)) {
                item.put("cmd", "WELCOME");
                item.put("username", tmp.get("nn") != null ? tmp.get("nn") : tmp.get("nickname"));
                item.put("face", "https://apic.douyucdn.cn/upload/" + tmp.get("ic"));
                item.put("content", "uenter".equals(type) ? "进入了直播间" : "分享了直播间");
                item.put("time", sdf.format(new Date()));
                item.put("nameColor", null);
                item.put("contentColor", null);
                item.put("identities", new JSONArray());
                item.getJSONArray("identities").add(0);
            }
            if (item.keySet().size() == 0) continue;
            // 将头像链接转为base64数据对象，防止跨域
            if (item.get("face") != null)
                try {
                    item.put("avatar", "data:image/png;base64," + Base64.getEncoder().encodeToString(
                            new RestTemplate().getForObject(item.getString("face") + "_big.jpg", byte[].class)));
                } catch (Exception ignored) {
                }
            JSONArray result = new JSONArray();
            result.add(item);
            this.sendMsg(result.toJSONString());
        }
    }
}
